package org.pi.common.vision.tool

import org.pi.common.matrix.BooleanMatrix
import org.pi.common.matrix.Api._

trait BinaryMorphologyToolTrait {
	/* applies w times a 1d masks with ones on a binary image. */
	private def and1d2d(image1: Array[Boolean],pos0: Int, w: Int, s: Int, filterLength: Int, image2: Array[Boolean]): Unit = {
 		var x: Int = 0;
 		var fx: Int = 0;
		var pos: Int = pos0;
		var pos1: Int = 0;
		var x1: Int = 0;
		var x2: Int = 0;
		var fsizex: Int = filterLength;
		var fsizex2: Int = Math.floor(fsizex.toFloat/2.0f).toInt;
		
		x=0;
		while (x<w) { // main loop
			image2(pos) = true;
			if (x>=fsizex2) x1 = 0
			else x1 = fsizex2-x
			if (w-x>fsizex-fsizex2) {
				x2 = fsizex
			}
			else {
				x2 = (w-x+fsizex2);
			}
			
			pos1 = pos0+(x+x1-fsizex2)*s;
			fx = x1;
			while ( fx < x2 ) {
				image2(pos) &= image1(pos1)
				pos1+=s
				fx += 1
			}
			
			pos+=s
			x+=1;
		} // end for (main loop)
	} // end def

	/* applies w times a 1d masks with ones on a binary image. */
	private def or1d2d(image1: Array[Boolean],pos0: Int, w: Int, s: Int, filterLength: Int, image2: Array[Boolean]): Unit = {
 		var x: Int = 0;
 		var fx: Int = 0;
		var pos: Int = pos0;
		var pos1: Int = 0;
		var x1: Int = 0;
		var x2: Int = 0;
		var fsizex: Int = filterLength;
		var fsizex2: Int = Math.floor(fsizex.toFloat/2.0f).toInt;
		
		x=0;
		while (x<w) { // main loop
			image2(pos) = false;
			if (x>=fsizex2) x1 = 0
			else x1 = fsizex2-x
			if (w-x>fsizex-fsizex2) {
				x2 = fsizex
			}
			else {
				x2 = (w-x+fsizex2);
			}
			
			pos1 = pos0+(x+x1-fsizex2)*s;
			fx = x1;
			while ( fx < x2 ) {
				image2(pos) |= image1(pos1)
				pos1+=s
				fx += 1
			}
			
			pos+=s
			x+=1;
		} // end for (main loop)
	} // end def

	def imerode1d2d(image1: BooleanMatrix, filterLength: Int, image2Param: BooleanMatrix, d: Int) : BooleanMatrix = {
		var x: Int = 0
		var y: Int = 0
		var t: Int = 0
		var pos: Int = 0
		var image2 = image2Param
		
		if (image1.shape.size!= 2) throw new Error("imerode1d2d only defined for 2d images")
		
		//if (image2==null) image2=new FloatMatrix( image1.shape : _*)
		if (image2==null) image2=new BooleanMatrix( image1.shape :_* )
		val w = image1.length(1)
		val h = image1.length(0)
		//println("w="+w+" h="+h)
		d match {
			case 0 =>
				pos = 0;
				y = 0;
				while (y < w) {
					and1d2d(image1.data,pos,h,1,filterLength,image2.data);
					pos+=h
					y+=1
				}
			case 1 =>
				pos = 0;
				x = 0;
				while (x < h) {
					and1d2d(image1.data,pos,w,h,filterLength,image2.data);
					pos+=1
					x+=1
				}
			case _ =>
				throw new Error("error, filter1d_2d: worng dimension!");
		}
		image2
	}

	def imdilate1d2d(image1: BooleanMatrix, filterLength: Int, image2Param: BooleanMatrix, d: Int) : BooleanMatrix = {
		var x: Int = 0
		var y: Int = 0
		var t: Int = 0
		var pos: Int = 0
		var image2 = image2Param
		
		if (image1.shape.size!= 2) throw new Error("imdilate1d2d only defined for 2d images")
		
		//if (image2==null) image2=new FloatMatrix( image1.shape : _*)
		if (image2==null) image2=new BooleanMatrix( image1.shape :_* )
		val w = image1.length(1)
		val h = image1.length(0)
		//println("w="+w+" h="+h)
		d match {
			case 0 =>
				pos = 0;
				y = 0;
				while (y < w) {
					or1d2d(image1.data,pos,h,1,filterLength,image2.data);
					pos+=h
					y+=1
				}
			case 1 =>
				pos = 0;
				x = 0;
				while (x < h) {
					or1d2d(image1.data,pos,w,h,filterLength,image2.data);
					pos+=1
					x+=1
				}
			case _ =>
				throw new Error("error, filter1d_2d: worng dimension!");
		}
		image2
	}

	def imerode(image1: BooleanMatrix, filterLengthX: Int, filterLengthY: Int) : BooleanMatrix = imerode(image1, filterLengthX, filterLengthY, null, null)
	def imerode(image1: BooleanMatrix, filterLengthX: Int, filterLengthY: Int, imageTParam: BooleanMatrix, image2Param: BooleanMatrix) : BooleanMatrix = {
		val tmp = imerode1d2d(image1, filterLengthX, imageTParam, 1)
		imerode1d2d(tmp, filterLengthY, image2Param, 0)
	}

	def imdilate(image1: BooleanMatrix, filterLengthX: Int, filterLengthY: Int) : BooleanMatrix = imdilate(image1, filterLengthX, filterLengthY, null, null)
	def imdilate(image1: BooleanMatrix, filterLengthX: Int, filterLengthY: Int, imageTParam: BooleanMatrix, image2Param: BooleanMatrix) : BooleanMatrix = {
		val tmp = imdilate1d2d(image1, filterLengthX, imageTParam, 1)
		imdilate1d2d(tmp, filterLengthY, image2Param, 0)
	}

	def imerode(image1: BooleanMatrix, filter: BooleanMatrix) : BooleanMatrix = imerode2d(image1, filter, null)
	def imerode2d(in:BooleanMatrix, kernel: BooleanMatrix, outParam: BooleanMatrix): BooleanMatrix = {
		if (in.shape.size!= 2) throw new Error("imerode2d only defined for 2d images")
		if (kernel.shape.size!= 2) throw new Error("imerode2d only defined for 2d filters")

		var out = outParam;
		if (out==null) out = new BooleanMatrix(in.shape :_*);
		if (out.hasSameShape(in)) out = new BooleanMatrix( in.shape :_* )

		var x: Int=0
		var y: Int=0
		var fx: Int=0
		var fy: Int=0
		var pos:Int=0
		var pos1:Int =0
		var fpos: Int =0
		var x1:Int =0
		var x2:Int=0
		var y1:Int=0
		var y2:Int=0
		val w = in.length(1)
		val h = in.length(0)
		
		val filter = kernel;
		
		var fsizex: Int = kernel.length(1);
		var fsizey: Int = kernel.length(0);
		var fsizex2: Int = Math.floor(fsizex.toDouble/2.0).toInt;
		var fsizey2:Int = Math.floor(fsizey.toDouble/2.0).toInt;
		
		pos=0;
		x=0;
		while(x<w) {
			y=0;
			while(y<h){
				var localResult:Boolean=true;
				x1 = if(x>=fsizex2) 0 else (fsizex2-x);
				x2 = if(w-x>fsizex-fsizex2) fsizex else (w-x+fsizex2);
				y1 = if(y>=fsizey2) 0 else (fsizey2-y);
				y2 = if(h-y>fsizey-fsizey2) fsizey else (h-y+fsizey2);
				fx=x1
				while(fx<x2) {
					fy=y1;
					fpos = fy+fx*fsizey;
					pos1 = (y+fy-fsizey2)+(x+fx-fsizex2)*h;
					while(fy<y2) {
						if (filter(fpos)) {
							localResult &= in(pos1);
						}
						fy+=1
						fpos+=1
						pos1+=1
					}
					fx+=1
				}
				out(pos) = localResult;
				y+=1
				pos+=1
			}
			x+=1;
		}

		out;
	}

	def imdilate(image1: BooleanMatrix, filter: BooleanMatrix) : BooleanMatrix = imdilate2d(image1, filter, null)
	def imdilate2d(in:BooleanMatrix, kernel: BooleanMatrix, outParam: BooleanMatrix): BooleanMatrix = {
		if (in.shape.size!= 2) throw new Error("imerode2d only defined for 2d images")
		if (kernel.shape.size!= 2) throw new Error("imerode2d only defined for 2d filters")

		var out = outParam;
		if (out==null) out = new BooleanMatrix(in.shape :_*);
		if (out.hasSameShape(in)) out = new BooleanMatrix( in.shape :_* )

		var x: Int=0
		var y: Int=0
		var fx: Int=0
		var fy: Int=0
		var pos:Int=0
		var pos1:Int =0
		var fpos: Int =0
		var x1:Int =0
		var x2:Int=0
		var y1:Int=0
		var y2:Int=0
		val w = in.length(1)
		val h = in.length(0)
		
		val filter = kernel;
		
		var fsizex: Int = kernel.length(1);
		var fsizey: Int = kernel.length(0);
		var fsizex2: Int = Math.floor(fsizex.toDouble/2.0).toInt;
		var fsizey2:Int = Math.floor(fsizey.toDouble/2.0).toInt;
		
		pos=0;
		x=0;
		while(x<w) {
			y=0;
			while(y<h){
				var localResult:Boolean=false;
				x1 = if(x>=fsizex2) 0 else (fsizex2-x);
				x2 = if(w-x>fsizex-fsizex2) fsizex else (w-x+fsizex2);
				y1 = if(y>=fsizey2) 0 else (fsizey2-y);
				y2 = if(h-y>fsizey-fsizey2) fsizey else (h-y+fsizey2);
				fx=x1
				while(fx<x2) {
					fy=y1;
					fpos = fy+fx*fsizey;
					pos1 = (y+fy-fsizey2)+(x+fx-fsizex2)*h;
					while(fy<y2) {
						if (filter(fpos)) {
							localResult |= in(pos1);
						}
						fy+=1
						fpos+=1
						pos1+=1
					}
					fx+=1
				}
				out(pos) = localResult;
				y+=1
				pos+=1
			}
			x+=1;
		}

		out;
	}
		
	/** create a circle mask */
	def circMask(radius: Int): BooleanMatrix = {
		val (mX,mY) = meshgrid( ((-radius)~(+radius)).toIntMatrix, ((-radius)~(+radius)).toIntMatrix )
		sqrt(mX*mX+mY*mY)<=radius
	}

	/** open with mask */
	def imopen(im: BooleanMatrix, mask: BooleanMatrix) : BooleanMatrix = imdilate( imerode(im, mask), mask)
	/** open with box mask */
	def imopen(im: BooleanMatrix, filterLengthX: Int, filterLengthY: Int) : BooleanMatrix = imdilate( imerode(im, filterLengthX, filterLengthY), filterLengthX, filterLengthY)

	/** close with mask */
	def imclose(im: BooleanMatrix, mask: BooleanMatrix) : BooleanMatrix = imerode( imdilate(im, mask), mask)
	/** close with box mask */
	def imclose(im: BooleanMatrix, filterLengthX: Int, filterLengthY: Int) : BooleanMatrix = imerode( imdilate(im, filterLengthX, filterLengthY), filterLengthX, filterLengthY)
}

object BinaryMorphologyTool extends BinaryMorphologyToolTrait {
}
